#include <cstdlib>
#include <cstdio>
#include <unistd.h>
#include <cerrno>
#include <cstring>
#include <ctime>
#include "Reactor.hh"
#include "Console.h"
#include "UniverseDatagram.hh"
#ifdef __linux__
#include <sys/epoll.h>
#else
#include <sys/event.h>
extern struct kevent changes[MAX_EVENTS + 1 + 1]; // 要监视的事件列表
#endif
#define MAX_EVENTS  1024
#define SERV_PORT   8080
extern struct JJ::myevent_s g_events[MAX_EVENTS+ 1 + 1];   /* +1 倒数第二个用于 listen fd */ /* 最后一个用于监听redis */
extern int g_efd;          /* epoll_create返回的句柄 */
bool isMain = false;
unsigned int slaveStartNum;
int main(int argc, char *argv[])
{
    if (argc < 3) {
        std::cerr << "use: UniverseRoom [master_port] [true slave_num|false]" << std::endl;
        return EXIT_FAILURE;
    }
    isMain = (strcmp(argv[2], "true") == 0);

    if (isMain) {
        if (argc != 4) {
            std::cerr << "use: UniverseRoom master_port true slave_num" << std::endl;
            return EXIT_FAILURE;

        }
        slaveStartNum = atoi(argv[3]);
    }
    unsigned short port = SERV_PORT; // default
    port = atoi(argv[1]);
#ifdef __linux__
    g_efd = epoll_create(MAX_EVENTS + 1 + 1);
#else
    g_efd = kqueue(); // kqueue对象
#endif
    if (g_efd <= 0) {
        printf("create efd in %s err %s\n", __func__, strerror(errno));
        return EXIT_FAILURE;
    }
    if (isMain) {
        JJ::initlistensocket(g_efd, port);
    } else {
        bool r = JJ::initclientsocket(g_efd, port);
        if (!r) {
            std::cout << strerror(errno) << std::endl;
            return EXIT_FAILURE;
        }
    }

    Console::welcome(port, isMain);

    RedisAsync& redis = *RedisAsync::getSingleton();

    redis.initSocket(g_efd);
    // init space
    Space::getSingleton();
    /* 事件循环 */
#ifdef __linux__
    struct epoll_event events[MAX_EVENTS + 1 + 1];
#else
    struct kevent events[MAX_EVENTS + 1 + 1];
#endif

    int checkpos = 0, i;
    while (true) {
        // 解析缓冲区
        JJ::update();
        redis.processRecv();
        /* 超时验证，每次测试100个链接，不测试listenfd 当客户端60秒内没有和服务器通信，则关闭此客户端链接 */
        long now = time(nullptr);
        for (i = 0; i < 100; i++, checkpos++) {
            if (checkpos == MAX_EVENTS)
                checkpos = 0;
            if (g_events[checkpos].status != 1)
                continue;
            long duration = now - g_events[checkpos].last_active;
            if (duration >= 10) {
                // 底层超时逻辑
//                close(g_events[checkpos].fd);
//                printf("[fd=%d] timeout\n", g_events[checkpos].fd);
//                eventdel(g_efd, &g_events[checkpos]);
            }
        }
        /* 等待事件发生 */
        int nfd;
#ifdef __linux__
        nfd = epoll_wait(g_efd, events, MAX_EVENTS+2, 1000);
#else
        struct timespec tv;
        tv.tv_sec = 1;
        tv.tv_nsec = 0;
        nfd = kevent(g_efd, nullptr, 0,
                     events, MAX_EVENTS + 2, &tv); // 已经就绪的文件描述符数量
#endif
        if (nfd < 0) {
            printf("epoll_wait error, exit, %s\n", strerror(errno));
            break;
        }
        for (i = 0; i < nfd; i++) {
#ifdef __linux__
            auto *ev = (JJ::myevent_s *)events[i].data.ptr;
            if ((events[i].events & EPOLLIN) && (ev->events & EPOLLIN)) {
                ev->recv_call_back(ev->fd, events[i].events, ev->arg);
            }
            if ((events[i].events & EPOLLOUT) && (ev->events & EPOLLOUT)) {
                ev->send_call_back(ev->fd, events[i].events, ev->arg);
            }
#else
            auto *ev = (JJ::myevent_s *)events[i].udata;
            if ((events[i].filter == EVFILT_READ) && (ev->events & EVFILT_READ)) {
                ev->recv_call_back(ev->fd, events[i].filter, ev->arg);
            }
            if ((events[i].filter == EVFILT_WRITE) && (ev->events & EVFILT_WRITE)) {
                ev->send_call_back(ev->fd, events[i].filter, ev->arg);
            }
            if ((events[i].filter == (EVFILT_WRITE + EVFILT_READ)) && (ev->events == (EVFILT_WRITE + EVFILT_READ))) {
                ev->recv_call_back(ev->fd, events[i].filter, ev->arg);
                ev->send_call_back(ev->fd, events[i].filter, ev->arg);
            }
#endif
        }
    }

    /* 退出前释放所有资源 */
    return 0;
}